#include <iostream>
#include <string>
#include <cassert>
#include <vector>
#include <cerrno>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "Task.hpp"

using namespace std;
const int gnum = 3;

Task t;

class Ctrl
{
public:
    pid_t _child_id; // 父进程控制的子进程，以及该子进程对应的写端
    int _write_fd;
    Ctrl(int id = 0, int fd = 0)
        : _child_id(id),
          _write_fd(fd)
    {
    }

    ~Ctrl()
    {
    }
};

void waitCtrl()
{
    int command = 0;
    while (true)
    {
        // cout << "befor read" << endl;
        int n = read(0, &command, sizeof(int));
        // cout << "after read" << endl;
        if (n > 0)
        {
            cout << "子进程 ： " << getpid() << "正在执行" << t._name[command] << "任务" << endl;
            t.Execute(command);
        }
        else if (n == 0)
        {
            cout << "写端关闭，我也退出了" << endl;
            break;
        }
        else
        {
            cout << "读取错误，我退出了" << endl;
            break;
        }
    }
}
void CreatProcess(vector<Ctrl> &ParCtrl)
{
    vector<int> fds;
    for (int i = 0; i < gnum; i++)
    {
        // 建立管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        // 创建子进程
        pid_t id = fork();
        assert(id >= 0);
        (void)id;
        if (id == 0) // 子进程
        {
            // 关闭fd，因继承问题，产生多余的fd
            for (auto e : fds)
                close(e);
            // 子进程  关闭写端，进行读数据
            close(pipefd[1]);

            // 更改输入重定向，以后进行读数据，可以直接从标准输入中获取，不在需要pipefd[0]
            // cout <<"pipefd[0] " << pipefd[0] << endl; 子进程共同使用同一个管道，子进程在管道中的读写指针共同进退

            dup2(pipefd[0], 0);
            waitCtrl();
            exit(0);
        }
        // 父进程 关闭读端，进行写数据
        close(pipefd[0]);

        // 为了管理这一批子进程，需要先描述在组织 -- class
        ParCtrl.push_back(Ctrl(id, pipefd[1]));

        // 由于继承的问题，随着，循环次数的增加，子进程，继承fd表中并不是一一对应的，也继承了之前父进程有的fd
        fds.push_back(pipefd[1]);
    }
}
int ShowBoard()
{
    cout << "*************************************" << endl;
    cout << "*************************************" << endl;
    cout << "**  0. NET_TASK       1. MYSQL_TASK**" << endl;
    cout << "**  2. SYNC_TASK      3. Quit      **" << endl;
    cout << "*************************************" << endl;
    cout << "*************************************" << endl;
    int command = 0;
    cin >> command;
    return command;
}
void CtrlProcess(vector<Ctrl> &ParCtrl)
{
    int command = 0;
    int index = 0;

    while (true)
    {
        int command = ShowBoard();

        if (command == 3)
            break;
        if (command < 0 || command > 2)
            continue;

        // int index = rand() % ParCtrl.size();//选择哪个子进程执行该任务
        // 每个子进程轮询
        int n = write(ParCtrl[index]._write_fd, &command, sizeof(int));
        index++;
        index %= ParCtrl.size();

        if (n > 0)
        {
            cout << "write successful" << endl;
        }
        else if (n == -1)
        {
            cout << "write filed" ;
            cout << "error: " << strerror(errno) << endl;
            exit(1);
        }
        sleep(1);
    }
}
void WaitProcess(vector<Ctrl> &ParCtrl)
{
    // 终止子进程的两种方案
    // 1.另外设置一个结束任务
    // 2.关闭写端，读端读到0个字节，自动退出，这里采用第二个方案
    for (auto e : ParCtrl)
    {
        close(e._write_fd);

        // 子进程退出后，需要对僵尸状态进行处理
        pid_t n = waitpid(e._child_id, nullptr, 0);
        if (n > 0)
            cout << "我是父进程，我成功等待子进程" << e._child_id << "退出" << endl;
        else
        {
            cout << "wait filed " ;
            cout << "error: " << strerror(errno) << endl;
        }
    }
}

int main()
{
    vector<Ctrl> ParCtrl;
    // 构建进程控制，父进程写数据，从而控制子进程
    CreatProcess(ParCtrl);
    // 父进程  写指令，控制子进程完成任务
    CtrlProcess(ParCtrl);
    WaitProcess(ParCtrl);
    return 0;
}